--------------------------------------------------------------------------------
-- Team:		Synapse
-- Module:	ALU
--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;


entity ALU is
    Port ( A		:	in std_logic_vector(15 downto 0);
           B		:	in std_logic_vector(15 downto 0);
			  opcode	:	in std_logic_vector(2 downto 0);
			  clk		:	in std_logic;
			  reset	:	in std_logic;
           S		:	out std_logic_vector(15 downto 0);
			  
			  flagF	:	out std_logic;
			  flagL	:	out std_logic;
			  flagC	:	out std_logic;
			  flagN	:	out std_logic;
			  flagZ	:	out std_logic);
end ALU;

architecture Behavioral of ALU is

 	signal A_plus_B : std_logic_vector(16 downto 0);
	signal A_minus_B : std_logic_vector(16 downto 0);
	signal A_and_B : std_logic_vector(15 downto 0);
	signal A_or_B : std_logic_vector(15 downto 0) ;
        signal	B_minus_A_zero:std_logic_vector(16 downto 0);
        signal	B_minus_A_sign:std_logic_vector(16 downto 0);
	signal A_xor_B : std_logic_vector(15 downto 0);

begin
     
	A_plus_B <= (A(15) & A) + (B(15) & B);
	A_minus_B <= (A(15) & A) - (B(15) & B);
	B_minus_A_sign <= (B(15) & B) - (A(15) & A);	-- used for the rest of the flags
	B_minus_A_zero <= ('0' & B) - ('0' & A);		-- used to calculate the L flag

    	A_and_B <= A AND B;
	A_or_B <= A OR B;
	A_xor_B <= A XOR B;
	process (A,B,opcode)
	begin

	
		if (opcode = "000") then
				S <= A_plus_B(15 downto 0);
			      
			-- SUB
		elsif (opcode = "001") then
				S <= A_minus_B(15 downto 0);
			
			-- AND
		 elsif (opcode = "010") then
				S <= A_and_B;

			-- OR
		 elsif (opcode = "011") then
				S <= A_or_B;

			-- XOR
		elsif (opcode = "100") then
				S <= A_xor_B;

			-- MOV
	         elsif (opcode = "111") then
				S <= B;

			-- Shifts
		 elsif (opcode = "110") then
			 if (B(4) = '1') then
				-- Right Shift
					S <= "0" & A(15 downto 1);
				else
					-- Left Shift
					S <= A(14 downto 0) & "0";
				end if;

		 
                 else
                    S <= "0000000000000000";

		end if;

	end process;
         
 	process (clk, reset)
	begin

		if (reset = '0') then
			flagF <= '0';
			flagL <= '0';
			flagC <= '0';
			flagN <= '0';
			flagZ <= '0';

		elsif (rising_edge(clk)) then
			-- Opcode = '000': ADD
			-- Opcode = '001': SUB
			-- Opcode = '010': AND
			-- Opcode = '011': OR
			-- Opcode = '100': XOR
			-- Opcode = '101': CMP
			-- Opcode = '110': LSH, A << B
			-- Opcode = '111': MOV

			-- ADD
			if (opcode = "000") then
			     
				flagC <= A_plus_B(16);
				if ((A(15) XOR B(15)) = '1') then
					flagF <= '0';
				else
					flagF <= B(15) XOR A_plus_B(15);
				end if;
		
			-- SUB
			elsif (opcode = "001") then
			
				flagC <= A_minus_B(16);
				if ((A(15) XOR B(15)) = '1') then
					flagF <= '0';
				else
					flagF <= B(15) XOR A_minus_B(15);
				end if;

		
			-- CMP
			elsif (opcode = "101") then
				flagL <= B_minus_A_zero(16);
				flagN <= B_minus_A_sign(16) XOR A(15) XOR B(15);
				if (B_minus_A_sign = "00000000000000000") then
					flagZ <= '1';
				else
					flagZ <= '0';
				end if;

			-- MOV
			end if;

		end if;

	end process;
         
   
end;
